﻿// Copyright (c) 2011 Blue Onion Software, All rights reserved
using System;
using System.IO;
using System.Text;
using BlueOnionSoftware.Bloget;
using NUnit.Framework;

namespace BlogetTests
{
    [TestFixture]
    public class NCMemoryStreamUnitTests
    {
        private byte[] buffer;
        private NonContiguousMemoryStream disposedStream;
        private NonContiguousMemoryStream ncMemoryStream;

        [TestFixtureSetUp]
        public void Setup()
        {
            var random = new Random();
            buffer = new byte[1024*1024*2];
            random.NextBytes(buffer);
            ncMemoryStream = new NonContiguousMemoryStream();
            disposedStream = new NonContiguousMemoryStream();
            disposedStream.Close();
        }

        [TestFixtureTearDown]
        public void Finish()
        {
            ncMemoryStream.Dispose();
        }

        [Test]
        public void CanWriteTest()
        {
            Assert.IsTrue(ncMemoryStream.CanWrite);
        }

        [Test]
        public void CanReadTest()
        {
            Assert.IsTrue(ncMemoryStream.CanRead);
        }

        [Test]
        public void CanSeekTest()
        {
            Assert.IsTrue(ncMemoryStream.CanSeek);
        }

        [Test]
        public void WriteTest()
        {
            ncMemoryStream.Position = 0;
            ncMemoryStream.Write(buffer, 0, buffer.Length);
            Assert.AreEqual(buffer.Length, ncMemoryStream.Length);
        }

        [Test]
        public void ReadTest()
        {
            ncMemoryStream.Position = 0;
            ncMemoryStream.Write(buffer, 0, buffer.Length);
            var readBuffer = new byte[buffer.Length];
            ncMemoryStream.Position = 0;
            var count = ncMemoryStream.Read(readBuffer, 0, readBuffer.Length);
            Assert.AreEqual(buffer.Length, count);

            for (var i = 0; i < count; ++i)
            {
                Assert.AreEqual(buffer[i], readBuffer[i]);
            }
        }

        [Test]
        [ExpectedException(typeof (ArgumentNullException))]
        public void WriteParameter1Test()
        {
            ncMemoryStream.Write(null, 0, buffer.Length);
        }

        [Test]
        [ExpectedException(typeof (ArgumentOutOfRangeException))]
        public void WriteParameter2Test()
        {
            ncMemoryStream.Write(buffer, -1, buffer.Length);
        }

        [Test]
        [ExpectedException(typeof (ArgumentOutOfRangeException))]
        public void WriteParameter3Test()
        {
            ncMemoryStream.Write(buffer, 0, buffer.Length + 1);
        }

        [Test]
        [ExpectedException(typeof (ArgumentOutOfRangeException))]
        public void WriteParameter4Test()
        {
            ncMemoryStream.Write(buffer, 0, -1);
        }

        [Test]
        [ExpectedException(typeof (ArgumentOutOfRangeException))]
        public void WriteParameter5Test()
        {
            ncMemoryStream.Write(buffer, 100, buffer.Length);
        }

        [Test]
        [ExpectedException(typeof (ArgumentNullException))]
        public void ReadParameter1Test()
        {
            ncMemoryStream.Read(null, 0, buffer.Length);
        }

        [Test]
        [ExpectedException(typeof (ArgumentOutOfRangeException))]
        public void ReadParameter2Test()
        {
            ncMemoryStream.Read(buffer, -1, buffer.Length);
        }

        [Test]
        [ExpectedException(typeof (ArgumentOutOfRangeException))]
        public void ReadParameter3Test()
        {
            ncMemoryStream.Read(buffer, 0, buffer.Length + 1);
        }

        [Test]
        [ExpectedException(typeof (ArgumentOutOfRangeException))]
        public void ReadParameter4Test()
        {
            ncMemoryStream.Read(buffer, 0, -1);
        }

        [Test]
        [ExpectedException(typeof (ArgumentOutOfRangeException))]
        public void ReadParameter5Test()
        {
            ncMemoryStream.Read(buffer, 100, buffer.Length);
        }

        [Test]
        public void ShortStreamWriterReaderTest()
        {
            var tempBuffer = "I'm a buffer";

            using (var ncs = new NonContiguousMemoryStream())
            {
                using (var stream = new StreamWriter(ncs))
                {
                    stream.Write(tempBuffer);
                    stream.Flush();
                    ncs.Seek(0, SeekOrigin.Begin);

                    using (var reader = new StreamReader(ncs))
                    {
                        var result = reader.ReadToEnd();
                        Assert.IsTrue(tempBuffer == result);
                    }
                }
            }
        }

        [Test]
        public void LongStreamWriterReaderTest()
        {
            var tempBuffer = new string('\x00', 200000);

            using (var ncs = new NonContiguousMemoryStream())
            {
                using (var stream = new StreamWriter(ncs))
                {
                    stream.Write(tempBuffer);
                    stream.Flush();
                    ncs.Seek(0, SeekOrigin.Begin);

                    using (var reader = new StreamReader(ncs))
                    {
                        var result = reader.ReadToEnd();
                        Assert.IsTrue(tempBuffer == result);
                    }
                }
            }
        }

        [Test]
        public void ShortStringTest()
        {
            using (var stream = new NonContiguousMemoryStream())
            {
                var start = "a short string";
                var bytes = Encoding.ASCII.GetBytes(start);
                stream.Write(bytes, 0, bytes.Length);
                stream.Position = 0;
                using (var reader = new StreamReader(stream))
                {
                    var end = reader.ReadToEnd();
                    Assert.AreEqual(start, end);
                }
            }
        }

        [Test]
        public void LongStringTest()
        {
            using (var stream = new NonContiguousMemoryStream())
            {
                var start = new string('c', 20000);
                var bytes = Encoding.ASCII.GetBytes(start);
                stream.Write(bytes, 0, bytes.Length);
                stream.Position = 0;
                using (var reader = new StreamReader(stream))
                {
                    var end = reader.ReadToEnd();
                    Assert.AreEqual(start, end);
                }
            }
        }

        [Test]
        public void ConstructWithData()
        {
            var stream = new NonContiguousMemoryStream(buffer);
            Assert.AreEqual(buffer.Length, stream.Length);
        }

        [Test]
        [ExpectedException(typeof (ArgumentNullException))]
        public void ConstructWithNullData()
        {
            var stream = new NonContiguousMemoryStream(null);
        }

        [Test]
        public void LongWriteToTest()
        {
            var source = new NonContiguousMemoryStream(buffer);
            var sink = new MemoryStream();
            source.WriteTo(sink);
            Assert.AreEqual(buffer.Length, source.Length);
            Assert.AreEqual(source.Length, sink.Length);
        }

        [Test]
        public void ShortWriteToTest()
        {
            var bytes = new byte[] {0x00, 0x01, 0x02};
            var source = new NonContiguousMemoryStream(bytes);
            var sink = new MemoryStream();
            source.WriteTo(sink);
            Assert.AreEqual(bytes.Length, source.Length);
            Assert.AreEqual(source.Length, sink.Length);
        }

        [Test]
        [ExpectedException(typeof (NotImplementedException))]
        public void SetLengthTest()
        {
            ncMemoryStream.SetLength(1);
        }

        [Test]
        [ExpectedException(typeof (ArgumentNullException))]
        public void WriteToNullTest()
        {
            ncMemoryStream.WriteTo(null);
        }

        [Test]
        public void WriteByteTest()
        {
            using (Stream stream = new NonContiguousMemoryStream())
            {
                stream.WriteByte(0x00);
                Assert.AreEqual(1, stream.Length);
            }
        }

        [Test]
        [ExpectedException(typeof (InvalidDataException))]
        public void PositionInvalidTest()
        {
            ncMemoryStream.Position = -1;
        }

        [Test]
        public void SeekBeginTest()
        {
            ncMemoryStream.Seek(0, SeekOrigin.Begin);
            Assert.AreEqual(0, ncMemoryStream.Position);
        }

        [Test]
        public void SeekCurrentTest()
        {
            var start = ncMemoryStream.Position;
            ncMemoryStream.Seek(0, SeekOrigin.Current);
            Assert.AreEqual(start, ncMemoryStream.Position);
        }

        [Test]
        public void SeekEndTest()
        {
            ncMemoryStream.Seek(0, SeekOrigin.End);
            Assert.AreEqual(ncMemoryStream.Length, ncMemoryStream.Position);
        }

        [Test]
        public void SeekEndZeroLengthTest()
        {
            // Test zero length boundary condition
            using (Stream stream = new NonContiguousMemoryStream())
            {
                stream.Seek(0, SeekOrigin.End);
                Assert.AreEqual(0, stream.Position);
            }
        }

        [Test]
        [ExpectedException(typeof (IOException))]
        public void SeekInvalidBeginTest()
        {
            ncMemoryStream.Seek(-1, SeekOrigin.Begin);
        }

        [Test]
        [ExpectedException(typeof (ArgumentException))]
        public void SeekInvalidSeekOriginTest()
        {
            ncMemoryStream.Seek(0, SeekOrigin.End + 1);
        }

        #region Disposal Tests

        [Test]
        [ExpectedException(typeof (ObjectDisposedException))]
        public void WriteByteDisposeTest()
        {
            disposedStream.WriteByte(0x00);
        }

        [Test]
        [ExpectedException(typeof (ObjectDisposedException))]
        public void WriteToDisposeTest()
        {
            disposedStream.WriteTo(null);
        }

        [Test]
        [ExpectedException(typeof (ObjectDisposedException))]
        public void CanReadDisposeTest()
        {
            var test = disposedStream.CanRead;
        }

        [Test]
        [ExpectedException(typeof (ObjectDisposedException))]
        public void CanSeekDisposeTest()
        {
            var test = disposedStream.CanSeek;
        }

        [Test]
        [ExpectedException(typeof (ObjectDisposedException))]
        public void CanWriteDisposeTest()
        {
            var test = disposedStream.CanWrite;
        }

        [Test]
        [ExpectedException(typeof (ObjectDisposedException))]
        public void LengthDisposeTest()
        {
            var length = disposedStream.Length;
        }

        [Test]
        [ExpectedException(typeof (ObjectDisposedException))]
        public void PositionSetDisposeTest()
        {
            disposedStream.Position = 0;
        }

        [Test]
        [ExpectedException(typeof (ObjectDisposedException))]
        public void PositionGetDisposeTest()
        {
            var position = disposedStream.Position;
        }

        [Test]
        [ExpectedException(typeof (ObjectDisposedException))]
        public void ReadDisposeTest()
        {
            disposedStream.Read(null, 0, 0);
        }

        [Test]
        [ExpectedException(typeof (ObjectDisposedException))]
        public void SeekDisposeTest()
        {
            disposedStream.Seek(0, SeekOrigin.Begin);
        }

        [Test]
        [ExpectedException(typeof (ObjectDisposedException))]
        public void WriteDisposeTest()
        {
            disposedStream.Write(null, 0, 0);
        }

        #endregion
    }
}